/*! ******************************************************************************
 *
 * Created on 2022-08-01
 *
 * Copyright(c) YuZhou Big Data Laboratory CO.,Ltd, 2022.  All rights reserved.
 *
 *******************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ******************************************************************************/

package org.yzbdl.lanius.ui.plugin.dialog;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ConcurrentModificationException;
import java.util.concurrent.atomic.AtomicInteger;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.*;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.logging.LogChannel;
import org.pentaho.di.core.logging.LogChannelInterface;
import org.pentaho.di.ui.core.gui.GUIResource;
import org.pentaho.di.ui.spoon.Spoon;
import org.pentaho.di.ui.util.DialogPositionUtil;
import org.yzbdl.lanius.core.plugins.install.PluginInstaller;
import org.yzbdl.lanius.core.plugins.install.PluginLoader;
import org.yzbdl.lanius.core.plugins.metastore.LdrPluginEntity;
import org.yzbdl.lanius.ui.spoon.middle.ext.progress.ProgressCenter;
import org.yzbdl.lanius.ui.spoon.middle.ext.progress.ProgressComposite;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;

/**
 * 插件批量安装
 *
 * @author tanpeng@yzbdl.ac.cn
 * @version 1.0
 * @date 2022-08-02 11:19
 */
public class PluginBatchInstallDialog extends Dialog {

    protected static final LogChannelInterface LOGGER = new LogChannel("PluginBatchInstallDialog");

    private static final String ZIP = ".zip";

    private final Font font = new Font(null, "微软雅黑", 10, SWT.NORMAL);

    private Shell parent;

    private Shell shell;

    private static final int STYLE = SWT.DIALOG_TRIM | SWT.RESIZE;

    private int width, height;

    private Text labelPath, installLogs;
    private Button browserBtn, closeBtn;

    public PluginBatchInstallDialog() {
        super(Spoon.getInstance().getShell(), STYLE);
        this.parent = Spoon.getInstance().getShell();
        setText("插件安装对话框");
        this.width = 880;
        this.height = 768;
    }

    /**
     * 创建对话框
     * @param parent 父对象
     * @param style 传递风格
     */
    public PluginBatchInstallDialog(Shell parent, int style) {
        super(parent, style);
        setText("插件安装对话框");
    }

    /**
     * 打开对话框
     */
    public void open() {
        Display display = parent.getDisplay();
        createContents();
        shell.layout();
        shell.open();
        // 一旦安装只能等待安装结束
        shell.addShellListener(new ShellAdapter() {
            @Override
            public void shellClosed(ShellEvent e) {
                e.doit = closeBtn.getEnabled();
                if (!closeBtn.getEnabled()) {
                    setMessage("插件安装中，请稍后！", Const.MESSAGE_WARNING);
                }
            }
        });
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }

    /**
     * 创建对话框内容
     */
    private void createContents() {
        shell = new Shell(parent, STYLE);
        shell.setText("批量安装插件");
        shell.setImage(GUIResource.getInstance().getImageShellTitle());
        shell.setBackground(GUIResource.getInstance().getColorWhite());
        // 设置窗体大小和位置
        DialogPositionUtil.getXyPoint(parent, width, height, shell);
        shell.setLayout(new GridLayout());

        Composite batchInstallComp = new Composite(shell, SWT.NONE);
        batchInstallComp.setBackground(GUIResource.getInstance().getColorWhite());
        batchInstallComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        RowLayout rlBatchInstallComp = new RowLayout();
        rlBatchInstallComp.marginTop = 20;
        rlBatchInstallComp.spacing = 13;
        batchInstallComp.setLayout(rlBatchInstallComp);

        /* 顶部第一行 */
        drawFolderSelect(batchInstallComp);

        /* 顶部第二行 */
        drawMonitorComp();

        /* 底部工具栏 */
        drawTools();

    }

    /**
     * 底部工具栏
     */
    private void drawTools() {
        Composite toolComp = new Composite(shell, SWT.NONE);
        toolComp.setBackground(GUIResource.getInstance().getColorWhite());
        closeBtn = new Button(toolComp, SWT.NONE);
        closeBtn.setFont(font);
        closeBtn.setText("关闭");
        closeBtn.setSize(90, 36);
        closeBtn.setLocation(760, 15);
        closeBtn.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                dispose();
            }
        });
    }

    /**
     * 绘制监控
     */
    private void drawMonitorComp() {
        Composite progressComp = new Composite(shell, SWT.NONE);
        progressComp.setBackground(GUIResource.getInstance().getColorWhite());
        progressComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        progressComp.setLayout(new RowLayout());

        Composite progressTextComp = new Composite(progressComp, SWT.NONE);
        progressTextComp.setBackground(GUIResource.getInstance().getColorWhite());
        RowLayout rlProgressTextComp = new RowLayout(SWT.VERTICAL);
        rlProgressTextComp.marginTop = 8;
        rlProgressTextComp.marginLeft = 0;
        rlProgressTextComp.justify = true;
        progressTextComp.setLayout(rlProgressTextComp);
        Label installProgressText = new Label(progressTextComp, SWT.HORIZONTAL);
        installProgressText.setFont(font);
        installProgressText.setLayoutData(new RowData(80, SWT.DEFAULT));
        installProgressText.setBackground(GUIResource.getInstance().getColorWhite());
        installProgressText.setText("安装进度：");

        ProgressComposite installProgressBar = new ProgressComposite(progressComp, SWT.NONE);
        installProgressBar.setLayoutData(new RowData(760, 42));

        Composite logComp = new Composite(shell, SWT.NONE);
        logComp.setBackground(GUIResource.getInstance().getColorWhite());
        logComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        logComp.setLayout(new RowLayout());
        installLogs = new Text(logComp, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.CANCEL);
        installLogs.setBackground(GUIResource.getInstance().getColorWhite());
        installLogs.setEditable(false);
        installLogs.setLayoutData(new RowData(813, 480));
    }

    /**
     * 绘制目录选择框
     * @param batchInstallComp 父容器
     */
    private void drawFolderSelect(Composite batchInstallComp) {
        // 文本框加容器用于上下对齐
        Composite labelComp = PluginInstallDialog.getLabelCenterComp(batchInstallComp);
        Label labelText = new Label(labelComp, SWT.HORIZONTAL);
        labelText.setBackground(GUIResource.getInstance().getColorWhite());
        labelText.setLayoutData(new RowData(80, 36));
        labelText.setFont(font);
        labelText.setText("插件目录：");

        Composite labelPathComp = PluginInstallDialog.getTextCenterComp(batchInstallComp);
        labelPath = new Text(labelPathComp, SWT.SINGLE);
        labelPath.setBackground(GUIResource.getInstance().getColorWhite());
        labelPath.setLayoutData(new RowData(614, 24));
        labelPath.setEditable(false);
        labelPath.setMessage("选择目录");

        browserBtn = new Button(batchInstallComp, SWT.NONE);
        browserBtn.setLayoutData(new RowData(100, 36));
        browserBtn.setFont(font);
        browserBtn.setText("浏览目录");
        browserBtn.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                DirectoryDialog dialog = new DirectoryDialog(shell, SWT.OPEN);
                String path = dialog.open();
                if (path != null) {
                    labelPath.setText(path);
                    installLogs.setText("");
                    initPluWithDirOrFile(path);
                }
            }
        });
    }

    /**
     * 根据路径读取插件信息
     *
     * @param path zip路径或目录路径
     */
    private void initPluWithDirOrFile(String path) {
        try {
            // refresh();
            File file = new File(path);
            if (!file.exists()) {
                throw new FileNotFoundException("目标文件或目录不存在");
            } else if (!file.isDirectory() && !file.getAbsolutePath().endsWith(ZIP)) {
                throw new KettleException("目标文件类型不支持");
            }
            if (file.isDirectory()) {
                // 目标是目录
                batchImportAndLoadPlugin(file);
            }
        } catch (Exception e) {
            e.printStackTrace();
            setMessage("操作失败:" + e.getMessage(), Const.MESSAGE_ERROR);
        }
    }

    /**
     * 导入目录下的所有压缩包（目前不考虑支持递归文件夹）并安装
     *
     * @param folder 目标目录
     */
    private void batchImportAndLoadPlugin(File folder) {
        File[] files = folder.listFiles();
        AtomicInteger totalZipNum = new AtomicInteger(0);
        AtomicInteger errNum = new AtomicInteger(0);
        AtomicInteger doneNum = new AtomicInteger(0);
        assert files != null;
        for (File file : files) {
            if (!file.isDirectory() && file.getAbsolutePath().endsWith(".zip")) {
                totalZipNum.incrementAndGet();
            }
        }
        try {
            String progressName = "插件批量安装";
            ProgressCenter.add(progressName, "", totalZipNum.intValue(), true, parameters -> {
                ProgressCenter.cancel(progressName);
                return null;
            }, null);
            closeBtn.setEnabled(false);
            browserBtn.setEnabled(false);
            ThreadUtil.execute(() -> {
                for (File file : files) {
                    if (!file.isDirectory() && file.getAbsolutePath().endsWith(".zip")) {
                        LdrPluginEntity ldrPluginEntity = null;
                        try {
                            ldrPluginEntity = PluginInstaller.importFromZip(file);
                            PluginLoader.loadOne(ldrPluginEntity);
                        } catch (Exception e) {
                            LOGGER.logError("插件安装失败：");
                            LOGGER.logError("插件安装失败：" + e);
                            errNum.incrementAndGet();
                        } finally {
                            doneNum.incrementAndGet();
                            LdrPluginEntity finalLdrPluginEntity = ldrPluginEntity;
                            Display.getDefault().syncExec(() -> {
                                try {
                                    ProgressCenter.setSelection(progressName, doneNum.intValue());
                                    assert finalLdrPluginEntity != null;
                                    installLogs.append(finalLdrPluginEntity.getInstallOutPrint() + Const.CR);
                                } catch (Exception e) {
                                    // ignore
                                }
                            });
                        }
                    }
                }
                Display.getDefault().syncExec(() -> {
                    try {
                        setMessage(String.format("插件安装结束，安装插件包共%s个，成功%s个，失败%s个！", totalZipNum.intValue(),
                            totalZipNum.intValue() - errNum.intValue(), errNum.intValue()), Const.MESSAGE_SUCCESS);
                        ProgressCenter.cancel(progressName);
                        // 如果存在打开的etl,离线批量安装完成刷新
                        if (ObjectUtil.isNotEmpty(Spoon.getInstance().getActiveMeta())) {
                            PluginInstallDialog.refreshOpenTab();
                        }
                        closeBtn.setEnabled(true);
                        browserBtn.setEnabled(true);
                    } catch (Exception e) {
                        LOGGER.logError("出现错误：{}", e);
                    }
                });
            });
        } catch (ConcurrentModificationException e) {
            setMessage("对不起，出现了点意外，请重试！", Const.MESSAGE_WARNING);
        }
    }

    /**
     * 设置提示信息
     *
     * @param msg 提示信息
     */
    private void setMessage(String msg, String msgType) {
        PluginInstallDialog.showMessage(msg, msgType, shell);
    }

    /**
     * 关闭对话框
     */
    public void dispose() {
        shell.dispose();
    }

    /**
     * 判断对话框是否关闭
     */
    public boolean isDisposed() {
        return this.shell.isDisposed();
    }
}
